/*
	PS2KB_main.c

	The main source for the built-in PS/2 Keyboard driver.

	Also covers general PS/2 controller initialisation, etc (where possible).

	This driver is a temporary measure implemented for use until the USB controllers and HID devices can be
	correctly used. The driver will operate on either the USB legacy implementation or the actual PS/2
	controller - whichever is the default for the platform. The BIOS should set this up depending on what
	port(s) the keyboard is plugged into.

	The driver does not use hardware IRQs. This is due to many issues that occured when trying to enable them
	both with an emulator and on real hardware. I believe this is due to the fact that the CPU is operating in
	Long Mode, rather than Real Mode or Protected Mode. It's difficult to say.

	Instead of using hardware IRQs, the driver uses a process that polls the keyboard directly every 'frame'. A
	frame will occur every 30ms - 100ms. This is adjustable within the source code.

	Author: Aidan Goddard 6/10/13
*/

#include"../general/driver_error_codes.h"
#include"../../klib/headers/ktypes.h"
#include"../../klib/headers/klib_keyboard_buffer.h"
#include"../../klib/headers/klib_message_passing.h"
#include"../../klib/headers/klib_proc_threads.h"

#include"../../setup/headers/printf.h"

#define KB_SIG_STOP			1
#define KB_SIG_NUM_LED		2
#define KB_SIG_SCR_LED		3
#define KB_SIG_CAP_LED		4

// global vars
static uint8_t msg_buffer[256];
static uint8_t msg_buffer_out[256];
static uint32_t driver_id;
static volatile uint32_t fail = 0;
static volatile uint32_t busy = 1;
static uint64_t client_id = 0x0000deadbeef0000;
static MSG_CHANNEL tqueue = 0;
static char key_name[16] = "kbchannel"; // get me a keyboard channel, now!!

// PS/2 KB main process routine
uint64_t PS2_KB_main(void)
{

	//__KLIB__SleepThisThread(200000);
	// started. Create a message queue
	MSG_CHANNEL queue = __KLIB__MSGAllocateChannel(key_name, 2553);
	if(queue == 0)
	{
		// set global fail var
		fail = 1;
		busy = 0;
		__KLIB__WaitThisThread();
	}

	// finished setting up, clear busy flag
	busy = 0;
/*
	// wait for signal from setup routine
	if(__KLIB__MSGServerRead(queue, msg_buffer) != MSG_SUCCESS)
	{
		fail = 1;
		//printf("\nFAIL");
		__KLIB__WaitThisThread();
	}
*/
	// set up the Keyboard
	// clear the buffer
	while((inb(0x64) & 1) == 1)
		inb(0x60);

	// signal recieved, can now start correctly
	while(1)
	{
		// check the queue for message
		if(__KLIB__MSGServerPeek(queue) == MSG_MSG_EXIST)
		{
			// check signal type
			__KLIB__MSGServerRead(queue, msg_buffer);
			if(msg_buffer[0] == KB_SIG_STOP)
			{
				// recieved stop signal
				// stop
				break;
			}
			else if(msg_buffer[0] == KB_SIG_NUM_LED)
			{
				// recieved toggle numlock LED signal
				// second byte in buffer indicates which state (1 for on, 0 for off)
				//printf("\nNUMLOCK %u", msg_buffer[1]);
			}
			else if(msg_buffer[0] == KB_SIG_SCR_LED)
			{
				// recieved toggle scrolllock LED signal
				// second byte in buffer indicates which state (1 for on, 0 for off)
				//printf("\nSCROLLLOCK %u", msg_buffer[1]);
			}
			else if(msg_buffer[0] == KB_SIG_CAP_LED)
			{
				// recieved toggle capslock LED signal
				// second byte in buffer indicates which state (1 for on, 0 for off)
				//printf("\nCAPSLOCK %u", msg_buffer[1]);
			}
			else
			{
				// unrecognised signal
			}
		}

		// sleep untill next frame
		__KLIB__SleepThisThread(50000);

		// probe the keyboard port for scan codes
		// get all bytes available in this frame
		uint64_t scan_buffer[256];
		uint32_t c = 0;
		while((inb(0x64) & 1) == 1)
		{
			// read in the first byte
			uint8_t first = inb(0x60);
			scan_buffer[c] = first;

			// check for multi-byte values
			if(first == 0xe0)
			{
				// get second byte
				while((inb(0x64) & 1) == 0){}
				uint8_t second = inb(0x60);
				scan_buffer[c] = (scan_buffer[c] << 8) + second;

				// check it for 4-byte values
				if((second == 0x2a) || (second == 0xb7))
				{
					// is a 4-byte value, stuff it in there
					while((inb(0x64) & 1) == 0){}
					scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
					while((inb(0x64) & 1) == 0){}
					scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
				}
			}
			else if(first == 0xe1)
			{
				// is a 6-byte value
				while((inb(0x64) & 1) == 0){}
				scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
				while((inb(0x64) & 1) == 0){}
				scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
				while((inb(0x64) & 1) == 0){}
				scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
				while((inb(0x64) & 1) == 0){}
				scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
				while((inb(0x64) & 1) == 0){}
				scan_buffer[c] = (scan_buffer[c] << 8) + inb(0x60);
			}

			// increase buffer index
			c++;

			// check if this buffer is full
			if(c == 256)
			{
				break;
			}
		}

		// send this to the system keyboard buffer
		__KLIB__KBBufferWrite(c, scan_buffer);

	}

	// loop has been left, need to clean up resources
	__KLIB__MSGDeallocateChannel(queue);

	// finished, wait indefinitly
	__KLIB__WaitThisThread();
	return 0;
}

// KB LED toggle functions
static void PS2_KB_toggle_num(int state)
{
	msg_buffer_out[0] = KB_SIG_NUM_LED;
	msg_buffer_out[1] = state;
	__KLIB__MSGClientSendSync(tqueue, msg_buffer_out, client_id);
	return;
}

static void PS2_KB_toggle_scr(int state)
{
	msg_buffer_out[0] = KB_SIG_SCR_LED;
	msg_buffer_out[1] = state;
	__KLIB__MSGClientSendSync(tqueue, msg_buffer_out, client_id);
	return;
}

static void PS2_KB_toggle_cap(int state)
{
	msg_buffer_out[0] = KB_SIG_CAP_LED;
	msg_buffer_out[1] = state;
	__KLIB__MSGClientSendSync(tqueue, msg_buffer_out, client_id);
	return;
}

// reset function
static void PS2_KB_reset(void *params)
{
}

// shutdown function
void PS2_KB_STOP()
{
	msg_buffer_out[0] = KB_SIG_STOP;
	__KLIB__MSGClientSendSync(tqueue, msg_buffer_out, client_id);
}
/*
// the PS/2 init function (called by setup routine to make sure everything worked)
uint32_t PS2_KB_INIT(uint32_t *error)
{
	// find the message channel when it's no longer busy
	*error = 0;
	while(busy == 1){}
	if(fail == 1)
	{
		return 0xffffffff;
	}
	tqueue = __KLIB__MSGFindChannel(key_name, 2553);
	if(tqueue == 0)
	{
		return 0xfffffffe;
	}
	*error = __KLIB__MSGClientBindChannel(tqueue, client_id);
	if(*error != MSG_SUCCESS)
	{
		return 0xfffffffd;
	}
	*error = __KLIB__MSGClientSendSync(tqueue, msg_buffer_out, client_id);
	if(*error != MSG_SUCCESS)
	{
		return 0xfffffffc;
	}
	__KLIB__SleepThisThread(100000);
	if(fail == 1)
	{
		return 0xfffffffb;
	}

	// register driver with the keyboard buffer
	*error = __KLIB__KBRegisterDriver(&PS2_KB_toggle_num, &PS2_KB_toggle_cap, &PS2_KB_toggle_scr, &PS2_KB_reset, &driver_id);
	if(*error != KB_SUCCESS)
	{
		return 0xfffffffa;
	}

	return DRIVER_STARTUP_SUCCESS;

}



// main PS/2 Init routine
uint32_t PS2KB_INIT()
{
	// check ACPI tables (FADT) for the IA-PC boot architecture flags field
	ACPI_FADT *fadt = ACPIGetFADTBase();
	uint8_t *flags_a = (uint8_t*)(fadt);
	flags_a += 109;

	// get the flags value
	uint16_t boot_flags = *((uint16_t*)flags_a);
	printf("\n0x%x\n", boot_flags);



	// first grab an ISR
	ISR_VECTOR isr = __KLIB__ISRRegister(&ISR_KB_PS2, IRQ_CPL_KERNEL, IRQ_GATE_INTERRUPT, IRQ_IST_7, 3);
	if(isr == IRQ_NO_ISR_IN_PRIORITY)
	{
		return 0xffffffff;
	}

	// find out which system GSI is mapped to the ISA PS/2 keyboard IRQ
	uint32_t gsi = __KLIB__GetGSIFromISA(1);
	if(gsi == IRQ_ISA_NOT_MAPPED)
	{
		return 0xfffffffe;
	}

	// map the IOAPIC gsi line to the ISR vector
	uint32_t map_error = __KLIB__IOAPIC_map_IRQ(gsi, isr);
	if(map_error == IRQ_GSI_IN_USE)
	{
		return 0xfffffffd;
	}
	if(map_error == IRQ_GSI_NOT_MAPPED)
	{
		return 0xfffffffc;
	}

	// set it to use scan code set 2
	while((inb(0x64) & 2) == 1){}
	outb(0x60, 0xf0);

	while((inb(0x64) & 1) == 0){}
	uint8_t ack = inb(0x60);

	while((inb(0x64) & 2) == 1){}
	outb(0x60, 0x02);

	while((inb(0x64) & 1) == 0){}
	ack = inb(0x60);

	// turn off scan code set 1 translation in the PS/2 controller
	while((inb(0x64) & 2) == 1){}
	outb(0x64, 0x20);
	while((inb(0x64) & 1) == 0){}
	uint8_t c = inb(0x60);
	c = c & 0x3f;
	while((inb(0x64) & 2) == 1){}
	outb(0x64, 0x60);
	while((inb(0x64) & 2) == 1){}
	outb(0x60, c);


	// unmask the GSI
	__KLIB__IOAPIC_unmask_IRQ(gsi);

	while(1)
	{
		while((inb(0x64) & 1) == 0){}
		printf(" 0x%x ", inb(0x60));
	}

	// everything went okay
	return DRIVER_STARTUP_SUCCESS;
}

// routine to fetch the scancodes from they keyboard into the buffer
void KBFetch(uint8_t key)
{
	//printf("\nKEYBOARD");

	// finished, return
	return;
}

// driver shutdown routine (actually disables the PS/2 driver)
void stop(void *params)
{

}

*/








